// 在软件开发中，经常使用util来代表公共的工具包
#pragma once

#include <iostream>
#include <string>
#include <atomic>
#include <fstream>
#include <vector>

#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/time.h>

#include <boost/algorithm/string.hpp>

// 存放临时文件的公共路径
const std::string temp_path = "./temp/";

namespace ns_util
{
    // 对路径操作的工具集合
    class PathUtil
    {
    public:
        PathUtil() {}
        ~PathUtil() {}

        // 添加文件路径的公共方法
        static std::string AddSuffix(const std::string& file_name, const std::string& suffix)
        {
            return temp_path + file_name + suffix;
        }

        // 编译时需要的文件名：Src，Exe，CompileError
        // 为 源文件cpp 添加完整路径+后缀名
        // 1234 --> ./temp/1234.cpp
        static std::string Src(const std::string& file_name)
        {
            return AddSuffix(file_name, ".cpp");
        }

        // 为 可执行文件exe 添加完整路径+后缀名
        // 1234 --> ./temp/1234.exe
        static std::string Exe(const std::string& file_name)
        {
            return AddSuffix(file_name, ".exe");
        }

        // 为 编译错误文件compile_err 添加完整路径+后缀名
        // 1234 --> ./temp/1234.compile_err
        static std::string CompileError(const std::string& file_name)
        {
            return AddSuffix(file_name, ".compile_err");
        }
        
        // 运行时需要的文件名：stdin，stdout，stderr
        // 为 标准输入文件stdin 添加完整路径+后缀名
        // 1234 --> ./temp/1234.stdin
        static std::string StdIn(const std::string& file_name)
        {
            return AddSuffix(file_name, ".stdin");
        }

        // 为 标准输出文件stdout 添加完整路径+后缀名
        // 1234 --> ./temp/1234.stdout
        static std::string StdOut(const std::string& file_name)
        {
            return AddSuffix(file_name, ".stdout");
        }

        // 为 标准错误文件stderr 添加完整路径+后缀名
        // 1234 --> ./temp/1234.stderr
        static std::string StdErr(const std::string& file_name)
        {
            return AddSuffix(file_name, ".stderr");
        }
    };

    // 时间工具集合
    class TimeUtil
    {
    public:
        TimeUtil() {}
        ~TimeUtil() {}

        // 获取秒级别的时间戳
        static std::string GetTimeStamp()
        {
            struct timeval _time;
            gettimeofday(&_time, nullptr);
            return std::to_string(_time.tv_sec);
        }

        // 获取毫秒级别的时间戳
        static std::string GetTimeMs()
        {
            struct timeval _time;
            gettimeofday(&_time, nullptr);
            return std::to_string(_time.tv_sec * 1000 + _time.tv_usec / 1000);
        }
    };    

    // 对文件操作的工具集合
    class FileUtil
    {
    public:
        FileUtil() {}
        ~FileUtil() {}

        // 检测文件是否存在
        static bool IsFileExists(const std::string& path_name)
        {
            // stat 系统调用用于获取文件属性，返回值可以判断文件是否存在
            struct stat st;
            if(stat(path_name.c_str(), &st) == 0)
            {
                // 返回值为 0 表示获取到了文件属性，表示存在这个可执行文件
                return true;
            }
            return false;
        }

        // 获取一个编号不重复的文件名称
        // 采用 毫秒级别的时间戳 + 原子性递增的id值 保证唯一性
        static std::string UniqueFileName()
        {
            std::string ms = TimeUtil::GetTimeMs();
            // 使用 std::atomic 来保证原子性递增的id值
            std::atomic_uint id(0);
            id++;
            return ms + "_" + std::to_string(id);
        }

        // 将 code 写入 target 文件中
        static bool WriteFile(const std::string& target, const std::string& content)
        {
            std::ofstream out(target);
            if(!out.is_open()) return false;
            out.write(content.c_str(), content.size());
            out.close();  // 不要忘记关闭文件
            return true;
        }

        // 读取一个文件 target ，将内容进行返回 content
        // 第三个参数 isKeepEndl 表示读取文件时是否保留 '\n'，默认不保留
        static bool ReadFile(const std::string& target, std::string* content, bool isKeepEndl = false)
        {
            std::ifstream in(target);
            if(!in.is_open()) return false;
            // getline 一次读取一行，且自动会跳过 '\n'
            // getline 内部重载了强制类型转化，即其返回的 true/false 会适配while循环
            std::string line;
            while(std::getline(in, line))
            {
                (*content) += line;
                (*content) += (isKeepEndl ? "\n" : "");
            }
            in.close();  // 不要忘记关闭文件
            return true;
        }
    };

    class StringUtil
    {
    public:
        StringUtil() {}
        ~StringUtil() {}

        // 第一个参数s：要切割的字符串
        // 第二个参数out：每一个下标表示按分隔符 sep 切割后的字符串
        // 第三个参数sep：表示分隔符
        static void SplitString(const std::string& s, std::vector<std::string>* out, const std::string& sep)
        {
            // 自己实现，太麻烦
            // if(s.empty()) return false;
            // int findpos = 0;
            // while(true)
            // {
            //     int pos = s.find(sep, findpos);
            //     std::string str = s.substr(findpos, pos - findpos);
            //     out->push_back(str);
            //     findpos = pos + sep.size();
            //     if(pos == std::string::npos) break;
            // }
            // return true;

            // 使用Boost库的字符串分割函数实现
            boost::split(*out, s, boost::is_any_of(sep), boost::algorithm::token_compress_on);
        }
    };
}